package com.scrm.server.wx.cp.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.scrm.api.wx.cp.dto.BrSopCountCustomerDTO;
import com.scrm.api.wx.cp.dto.WxMsgAttachmentDTO;
import com.scrm.api.wx.cp.dto.WxMsgTemplateCountCustomerDTO;
import com.scrm.api.wx.cp.entity.*;
import com.scrm.api.wx.cp.enums.SopRuleWayEnum;
import com.scrm.api.wx.cp.enums.SopTermEnum;
import com.scrm.api.wx.cp.enums.WxMsgSendStatusEnum;
import com.scrm.common.constant.Constants;
import com.scrm.common.dto.BatchDTO;
import com.scrm.common.exception.BaseException;
import com.scrm.common.util.DateUtils;
import com.scrm.common.util.JwtUtil;
import com.scrm.common.util.ListUtils;
import com.scrm.common.util.UUID;
import com.scrm.server.wx.cp.config.WxCpConfiguration;
import com.scrm.server.wx.cp.dto.*;
import com.scrm.server.wx.cp.entity.*;
import com.scrm.server.wx.cp.mapper.BrSopMapper;
import com.scrm.server.wx.cp.service.*;
import com.scrm.server.wx.cp.utils.CronUtil;
import com.scrm.server.wx.cp.utils.WxMsgUtils;
import com.scrm.server.wx.cp.vo.*;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.cp.api.impl.WxCpExternalContactServiceImpl;
import me.chanjar.weixin.cp.bean.external.contact.WxCpGroupMsgSendResult;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * sop 服务实现类
 * @author ouyang
 * @since 2022-04-17
 */
@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class BrSopServiceImpl extends ServiceImpl<BrSopMapper, BrSop> implements IBrSopService {

    @Autowired
    private IBrSopRuleService sopRuleService;

    @Autowired
    private IStaffService staffService;

    @Autowired
    private IWxTagService tagService;

    @Autowired
    private IWxCustomerService customerService;

    @Autowired
    private IWxCustomerStaffService wxCustomerStaffService;

    @Autowired
    private IXxlJobService xxlJobService;

    @Autowired
    private IDepartmentService departmentService;

    @Autowired
    private IBrSopDetailService detailService;

    @Autowired
    private IWxMsgTemplateService wxMsgTemplateService;

    @Autowired
    private IBrTodoService todoService;

    @Autowired
    private IWxTempFileService fileService;

    @Override
    public IPage<BrSopVO> pageList(BrSopPageDTO dto){
        LambdaQueryWrapper<BrSop> wrapper = new QueryWrapper<BrSop>()
        .lambda().eq(BrSop::getExtCorpId, dto.getExtCorpId())
                .like(StringUtils.isNotBlank(dto.getName()), BrSop::getName, dto.getName())
                .orderByDesc(BrSop::getCreatedAt);
        IPage<BrSop> page = page(new Page<>(dto.getPageNum(), dto.getPageSize()),wrapper);
        return page.convert(this::translation);
    }


    @Override
    public List<BrSop> queryList(BrSopQueryDTO dto){
        LambdaQueryWrapper<BrSop> wrapper = new QueryWrapper<BrSop>()
        .lambda().eq(BrSop::getExtCorpId, dto.getExtCorpId()).eq(BrSop::getStatus, dto.getStatus())
                .eq(BrSop::getTerm, dto.getTerm());
        return Optional.ofNullable(list(wrapper)).orElse(new ArrayList<>());
    }


    @Override
    public BrSopVO findById(String id){
        return translation(checkExists(id));
    }


    @Override
    public BrSop save(BrSopSaveDTO dto){

        //封装数据
        BrSop brSop = new BrSop();
        BeanUtils.copyProperties(dto,brSop);
        checkData(brSop);
        brSop.setId(UUID.get32UUID())
                .setUpdatedAt(new Date())
                .setCreatedAt(new Date())
                .setStatus(BrSop.ENABLE_STATUS)
                .setCreator(JwtUtil.getUserId());

        if (ListUtils.isNotEmpty(dto.getDepartmentIds())){
            brSop.setDepartmentIds(dto.getDepartmentIds().stream().map(Long::parseLong).collect(Collectors.toList()));
        }

        //入库
        save(brSop);

        //规则处理
        handleRule(brSop,dto.getRuleList());
        return brSop;
    }


    @Override
    public BrSop update(BrSopUpdateDTO dto){

        //校验参数
        BrSop old = checkExists(dto.getId());

        //封装数据
        BrSop brSop = new BrSop();
        BeanUtils.copyProperties(dto, brSop);
        checkData(brSop);
        brSop.setCreatedAt(old.getCreatedAt())
                .setCreator(old.getCreator())
                .setUpdatedAt(new Date())
                .setEditor(JwtUtil.getUserId());
        if (ListUtils.isNotEmpty(dto.getDepartmentIds())){
            brSop.setDepartmentIds(dto.getDepartmentIds().stream().map(Long::parseLong).collect(Collectors.toList()));
        }

        //入库
        updateById(brSop);

        //更新规则数据
        handleRule(brSop,dto.getRuleList());

        return brSop;
    }

    /**
     * 数据检查
     * @param brSop   brSop
     */
    private void checkData(BrSop brSop) {
        if (count(new LambdaQueryWrapper<BrSop>()
                .eq(BrSop::getExtCorpId, brSop.getExtCorpId())
                .ne(brSop.getId() != null, BrSop::getId, brSop.getId())
                .eq(BrSop::getName,brSop.getName()))>0){
            throw new BaseException("sop名称已存在，请重新命名！");
        }
        //校验触发条件
        if (SopTermEnum.getNameByCode(brSop.getTerm()) == null) {
            throw new BaseException("该触发条件不存在!");
        }

    }

    private void handleRule(BrSop brSop,List<BrSopRuleSaveOrUpdateDTO> ruleList) {

        //过滤用户
        WxMsgTemplateCountCustomerDTO dto = new WxMsgTemplateCountCustomerDTO();
        BeanUtils.copyProperties(brSop,dto);
        dto.setHasAllStaff(false);
        dto.setChooseTagType(WxMsgTemplate.CHOOSE_TYPE_ANY);
        List<WxCustomer> customerList = customerService.getCustomerListByCondition(dto);

        //移除已被删除的规则
        List<String> ruleIds = ruleList.stream().map(e -> e.getId()).collect(Collectors.toList());
        List<String> needDeleteRuleIds = Optional.ofNullable(sopRuleService.list(new LambdaQueryWrapper<BrSopRule>()
                .select(BrSopRule::getId)
                .eq(BrSopRule::getSopId, brSop.getId())
                .notIn(ListUtils.isNotEmpty(ruleIds), BrSopRule::getId, ruleIds)
        )).orElse(new ArrayList<>()).stream().map(BrSopRule::getId).collect(Collectors.toList());
        if (ListUtils.isNotEmpty(needDeleteRuleIds)){
            sopRuleService.batchDelete(new BatchDTO<String>().setIds(needDeleteRuleIds));
        }


        for (BrSopRuleSaveOrUpdateDTO rule : ruleList) {
            //校验规则参数
            if (StringUtils.isBlank(rule.getName())){
                throw new BaseException("规则名称不能为空");
            }
            Integer way = rule.getWay();
            if (way == null|| SopRuleWayEnum.getNameByCode(way) == null){
                throw new BaseException("执行方式不存在");
            }

            BrSopRule sopRule;
            //新增或更新规则表
            if (StringUtils.isNotBlank(rule.getId())){
                rule.setExtCorpId(brSop.getExtCorpId()).setEditor(brSop.getEditor());
                sopRule = sopRuleService.update(rule);
                //删除定时任务
                List<Integer> jobIds = rule.getJobId();
                if (ListUtils.isNotEmpty(jobIds)){
                    jobIds.forEach(jobId->{
                        xxlJobService.delete(jobId);
                    });
                }

            }else {
                rule.setExtCorpId(brSop.getExtCorpId()).setSopId(brSop.getId()).setCreator(brSop.getCreator());
                sopRule = sopRuleService.save(rule);
            }

            //筛选客户集合为空，无需创建定时任务
            if (ListUtils.isEmpty(customerList)){
                continue;
            }

            BrSopRuleParamDto paramDto = new BrSopRuleParamDto();
            BeanUtils.copyProperties(sopRule,paramDto);
            paramDto.setSopName(brSop.getName());
            paramDto.setSopStatus(brSop.getStatus());
            //存放生成定时任务id集合
            List<Integer> jobIdList = new ArrayList<>();
            //触发条件为添加好友
            if (SopTermEnum.ADD_FRIEND.getCode().equals(brSop.getTerm())){
                List<String> needCustomerIdList = new ArrayList<>();
                Map<String,Date> executeTimeMap = new HashMap<>();
                //计算这部分用户添加好友时间，小于设置天数则添加定时群发任务
                for (WxCustomer customer : customerList) {
                    Date executeTime = DateUtils.getDate(customer.getCreatedAt(), rule.getStartDay(), rule.getStartTime());
                    if (DateUtils.belongCalendarBefore(executeTime)){
                        String extId = customer.getExtId();
                        needCustomerIdList.add(extId);
                        executeTimeMap.put(extId,executeTime);
                    }
                }

                if (ListUtils.isEmpty(needCustomerIdList)){
                    continue;
                }
                //查询客户是哪个员工的
                List<WxCustomerStaff> customerStaffList = wxCustomerStaffService.list(new QueryWrapper<WxCustomerStaff>().lambda()
                        .in(WxCustomerStaff::getExtCustomerId, needCustomerIdList));
                List<String> staffIds = staffService.getStaffIdsByDepts(brSop.getExtCorpId(), brSop.getDepartmentIds(), brSop.getStaffIds());
                if (ListUtils.isNotEmpty(staffIds)){
                    //根据员工过滤客户
                    customerStaffList = customerStaffList.stream().filter(e -> staffIds.contains(e.getExtStaffId())).collect(Collectors.toList());
                }

                //创建定时群发任务<执行时间+员工id,客户id集合>
                Map<String, List<String>> sendIdMap = customerStaffList.stream().map(e -> {
                    BrSopDetail detail = new BrSopDetail();
                    detail.setExtStaffId(e.getExtStaffId()).setExtCustomerId(e.getExtCustomerId()).setExecuteAt(executeTimeMap.get(e.getExtCustomerId()));
                    return detail;
                }).collect(Collectors.groupingBy(i -> DateUtils.dateToStr(i.getExecuteAt()) + "_" + i.getExtStaffId(), Collectors.mapping(BrSopDetail::getExtCustomerId, Collectors.toList())));

                //<执行时间,<员工id，客户id集合>>
                Map<String, Map<String,List<String>>> map = new HashMap<>();
                sendIdMap.forEach((k,customerIds)->{
                            String[] split = k.split("_");
                            String executeTime = split[0];
                            String staffId = split[1];
                            if (!map.containsKey(executeTime)){
                                Map<String,List<String>> staffCustomerMap = new HashMap<>();
                                staffCustomerMap.put(staffId,customerIds);
                                map.put(executeTime,staffCustomerMap);
                            }else {
                                Map<String, List<String>> oldMap = map.get(executeTime);
                                oldMap.put(staffId,customerIds);
                                map.put(executeTime,oldMap);
                            }
                        }
                );

                sopRule.setPeriod(CronUtil.PERIOD_NEVER);
                if (SopRuleWayEnum.MASS.getCode().equals(way)){
                    //执行方式为群发，创建群发定时任务
                    map.forEach((executetime,idMap)->{
                                jobIdList.add(createXxlJob(idMap,DateUtils.strToDate(executetime),paramDto, Constants.WX_MSG_TASK_HANDLER,false,BrSopMsgTaskDto.SOP_TYPE));
                            }
                    );
                    rule.setJobId(jobIdList);
                }else if (SopRuleWayEnum.POST.getCode().equals(way)||SopRuleWayEnum.REMIND.getCode().equals(way)){
                    //执行方式为仅提醒或发朋友圈，创建应用内提醒定时任务
                    map.forEach((executetime,idMap)->{
                                jobIdList.add(createXxlJob(idMap,DateUtils.strToDate(executetime),paramDto,Constants.SEND_MSG_HANDLER,false,BrSopMsgTaskDto.SOP_TYPE));
                            }
                    );
                }
            }else if (SopTermEnum.TIME.getCode().equals(brSop.getTerm())){
                //触发条件为时间
                //查询客户是哪个员工的
                List<WxCustomerStaff> customerStaffList = wxCustomerStaffService.list(new QueryWrapper<WxCustomerStaff>().lambda()
                        .in(WxCustomerStaff::getExtCustomerId, customerList.stream().map(e->e.getExtId()).collect(Collectors.toList())));
                List<String> staffIds = staffService.getStaffIdsByDepts(brSop.getExtCorpId(), brSop.getDepartmentIds(), brSop.getStaffIds());
                if (ListUtils.isNotEmpty(staffIds)){
                    //根据员工过滤客户
                    customerStaffList = customerStaffList.stream().filter(e -> staffIds.contains(e.getExtStaffId())).collect(Collectors.toList());
                }

                //<员工id,客户id集合>
                Map<String, List<String>> sendIdList = customerStaffList.stream().collect(Collectors.groupingBy(WxCustomerStaff::getExtStaffId,
                        Collectors.mapping(WxCustomerStaff::getExtCustomerId, Collectors.toList())));
                //生成执行时间的任务
                if (DateUtils.belongCalendarBefore(rule.getExecuteAt())){
                    if (SopRuleWayEnum.MASS.getCode().equals(way)){
                        //执行方式为群发，创建群发任务
                        Integer jobId = createXxlJob(sendIdList, rule.getExecuteAt(), paramDto, Constants.WX_MSG_TASK_HANDLER,false,BrSopMsgTaskDto.SOP_TYPE);
                        jobIdList.add(jobId);
                    }else if (SopRuleWayEnum.POST.getCode().equals(way)||SopRuleWayEnum.REMIND.getCode().equals(way)){
                        //执行方式为仅提醒或发朋友圈，后台仅进行应用内提醒
                        Integer jobId = createXxlJob(sendIdList, rule.getExecuteAt(), paramDto, Constants.SEND_MSG_HANDLER,false,BrSopMsgTaskDto.SOP_TYPE);
                        jobIdList.add(jobId);
                    }
                }

                if (!CronUtil.PERIOD_NEVER.equals(rule.getPeriod())){
                        //生成周期定时任务,在执行时间当天执行
                    Integer periodJobId = createXxlJob(sendIdList, rule.getExecuteAt(), paramDto, Constants.CREATE_JOB_HANDLER,false,BrSopMsgTaskDto.SOP_TYPE);
                    jobIdList.add(periodJobId);

                    //生成停止周期的任务，在结束重复时间当天执行
                    Integer stopJobId = createStopJob(DateUtils.handle(rule.getExecuteAt(),rule.getEndAt()),sopRule.getId(), rule.getName(),sopRule.getCreator(),BrSopMsgTaskDto.SOP_TYPE);
                    jobIdList.add(stopJobId);
                }
            }
            //sop为启用状态，启用定时任务
            if (BrSop.ENABLE_STATUS.equals(brSop.getStatus())){
                jobIdList.forEach(jobId->xxlJobService.start(jobId));
            }
            //更新jobId字段
            sopRuleService.update(new UpdateWrapper<BrSopRule>().lambda()
                    .eq(BrSopRule::getId,sopRule.getId())
                    .set(BrSopRule::getJobId,jobIdList.toString()));
        }

    }

    @Override
    public Integer createStopJob(Date executeAt,String ruleId,String ruleName,String creator, Integer type){
        String cron = DateUtils.getCron(executeAt);
        XxlJobInfoDTO xxlJobInfoDTO = new XxlJobInfoDTO();
        Map<String,Object> param = new HashMap<>();
        param.put("ruleId",ruleId);
        param.put("type",type);
        Staff staff = staffService.find(creator);
        xxlJobInfoDTO.setJobDesc(ruleName).setAuthor(staff!=null?staff.getName():creator).setExecutorHandler(Constants.STOP_JOB_HANDLER)
                .setExecutorParam(JSON.toJSONString(param)).setCron(cron);
        return xxlJobService.addOrUpdate(xxlJobInfoDTO);
    }

    @Override
    public Integer createXxlJob(Map<String, List<String>> idMap,Date executeAt,BrSopRuleParamDto rule,String handleName,Boolean isPeriod,Integer sopType){
        String cron;
        if (isPeriod){
            cron = CronUtil.createCronExpression(executeAt, rule.getPeriod(), rule.getCustomDay());
        }else {
            cron = DateUtils.getCron(executeAt);
        }
        BrSopRuleParamDto ruleParamDto = new BrSopRuleParamDto();
        BeanUtils.copyProperties(rule,ruleParamDto);
        XxlJobInfoDTO xxlJobInfoDTO = new XxlJobInfoDTO();
        BrSopMsgTaskDto param = new BrSopMsgTaskDto();
        param.setIdMap(idMap).setExecuteAt(executeAt).setRule(ruleParamDto).setType(sopType).setSopName(rule.getSopName()).setSopStatus(rule.getSopStatus());
        Staff staff = staffService.find(rule.getCreator());
        xxlJobInfoDTO.setJobDesc(rule.getName()).setAuthor(staff!=null?staff.getName():rule.getCreator()).setExecutorHandler(handleName)
                .setExecutorParam(JSON.toJSONString(param)).setCron(cron);
        return xxlJobService.addOrUpdate(xxlJobInfoDTO);
    }

    @Override
    public void delete(String id){

        //校验参数
        BrSop brSop = checkExists(id);

        //删除
        removeById(id);

        //删除sop规则
        sopRuleService.deleteBySopId(id);
    }


    @Override
    public void batchDelete(BatchDTO<String> dto){

        //校验参数
        List<BrSop> brSopList = new ArrayList<>();
        Optional.ofNullable(dto.getIds()).orElse(new ArrayList<>()).forEach(id -> brSopList.add(checkExists(id)));

        List<String> ruleIdList = sopRuleService.list(new LambdaQueryWrapper<BrSopRule>().in(BrSopRule::getSopId, dto.getIds()))
                .stream().map(BrSopRule::getId).collect(Collectors.toList());
        if(ListUtils.isNotEmpty(ruleIdList)){
            sopRuleService.batchDelete(new BatchDTO<String>().setIds(ruleIdList));
        }

        //删除
        removeByIds(dto.getIds());
    }


    /**
     * 翻译
     * @param brSop 实体
     * @return BrSopVO 结果集
     * @author ouyang
     * @date 2022-04-17
     */
    private BrSopVO translation(BrSop brSop){
        BrSopVO vo = new BrSopVO();
        BeanUtils.copyProperties(brSop, vo);

        //翻译创建者
        Staff staff = staffService.find(brSop.getCreator());
        vo.setCreatorCN(staff!=null?staff.getName():brSop.getCreator());

        //翻译标签
        vo.setChooseTagNames(tagService.getNameByIds(brSop.getChooseTags()));

        //查询规则
        vo.setRuleList(sopRuleService.queryList(new BrSopRuleQueryDTO()
                .setExtCorpId(brSop.getExtCorpId()).setSopId(brSop.getId())));

        //查询选择的员工
        List<String> staffIds = brSop.getStaffIds();
        if (ListUtils.isNotEmpty(staffIds)){
            //员工信息
            List<Staff> staffList = staffService.list(new QueryWrapper<Staff>().lambda()
                    .eq(Staff::getExtCorpId, brSop.getExtCorpId())
                    .in(Staff::getExtId, staffIds));
            vo.setStaffList(staffList);
        }

        //查询选择的部门
        List<Long> departmentIds = brSop.getDepartmentIds();
        if (ListUtils.isNotEmpty(departmentIds)){
            List<Department> departmentList = departmentService.list(new QueryWrapper<Department>().lambda()
                    .eq(Department::getExtCorpId, brSop.getExtCorpId())
                    .in(Department::getExtId, departmentIds));
            vo.setDepartmentList(departmentList);
        }

        //查询选择的客户
        List<String> customerIds = brSop.getCustomerIds();
        if (ListUtils.isNotEmpty(customerIds)){
            //员工信息
            List<WxCustomer> customerList = customerService.list(new QueryWrapper<WxCustomer>().lambda()
                    .eq(WxCustomer::getExtCorpId, brSop.getExtCorpId())
                    .in(WxCustomer::getExtId, customerIds));
            vo.setCustomerList(customerList);
        }


        return vo;
    }


    @Override
    public BrSop checkExists(String id){
        if (StringUtils.isBlank(id)) {
            return null;
        }
        BrSop byId = getById(id);
        if (byId == null) {
            throw new BaseException("sop不存在");
        }
        return byId;
    }

    @Override
    public void updateStatus(BrSopUpdateStatusDTO dto) {
        BrSop brSop = checkExists(dto.getId());
        brSop.setStatus(dto.getStatus()).setUpdatedAt(new Date()).setEditor(JwtUtil.getUserId());
        updateById(brSop);

        //对定时任务禁用/启用
        List<BrSopRule> ruleList = sopRuleService.list(new LambdaQueryWrapper<BrSopRule>().eq(BrSopRule::getSopId, dto.getId()));
        List<Integer> jobIds = new ArrayList<>();
        if (!SopTermEnum.TIME.getCode().equals(brSop.getTerm())) {
            ruleList.stream().forEach(e -> {
                if (ListUtils.isNotEmpty(e.getJobId())) {
                    jobIds.addAll(e.getJobId());
                }
            });
        } else {
            ruleList.stream().forEach(e -> {
                if (ListUtils.isNotEmpty(e.getJobId())) {
                    if (!CronUtil.PERIOD_NEVER.equals(e.getPeriod())) {
                        if (DateUtils.belongCalendarBefore(DateUtils.handle(e.getExecuteAt(), e.getEndAt()))) {
                            jobIds.addAll(e.getJobId());
                        }
                    } else if (DateUtils.belongCalendarBefore(e.getExecuteAt())) {
                        jobIds.addAll(e.getJobId());
                    }
                }
            });
        }
        if (ListUtils.isEmpty(jobIds)){
            return;
        }
        //启用
        if (BrSop.ENABLE_STATUS.equals(dto.getStatus())){
            for (Integer jobId : jobIds) {
                xxlJobService.start(jobId);
            }

        }else if (BrSop.DISABLE_STATUS.equals(dto.getStatus())){
            //禁用
            for (Integer jobId : jobIds) {
                xxlJobService.stop(jobId);
            }
        }
    }

    @Override
    public BrSopPushDetailVO getPushDetail(String ruleId, String executeAt, String staffId, Integer jobId, String extCorpId) {
        List<BrSopDetail> list = detailService.list(new LambdaQueryWrapper<BrSopDetail>()
                .eq(BrSopDetail::getExtCorpId, extCorpId)
                .eq(BrSopDetail::getExtStaffId, staffId)
                .eq(BrSopDetail::getRuleId, ruleId)
                .eq(BrSopDetail::getJobId, jobId)
                .eq(BrSopDetail::getExecuteAt, executeAt));

        List<String> customerIds = Optional.ofNullable(list).orElse(new ArrayList<>()).stream().map(BrSopDetail::getExtCustomerId).collect(Collectors.toList());
        List<WxCustomer> customerList = new ArrayList<>();
        if (ListUtils.isNotEmpty(customerIds)){
            customerList = customerService.list(new QueryWrapper<WxCustomer>().lambda()
                    .eq(WxCustomer::getExtCorpId, extCorpId)
                    .in(WxCustomer::getExtId, customerIds));
        }

        BrSopPushDetailVO result = new BrSopPushDetailVO();
        BrSopDetail brSopDetail = list.get(0);

        //翻译文件
        List<WxMsgAttachmentDTO> mediaList = brSopDetail.getMsg().getMedia();
        if (ListUtils.isNotEmpty(mediaList)) {
            mediaList.stream().filter(e -> e.getFile() != null
                    && StringUtils.isNotBlank(e.getFile().getId()))
                    .forEach(e -> e.setFile(fileService.checkExists(e.getFile().getId())));
        }

        result.setMsg(brSopDetail.getMsg()).setSopName(brSopDetail.getSopName()).setCustomerList(customerList).setWay(brSopDetail.getWay()).setSendStatus(brSopDetail.getSendStatus());


        BrTodo brTodo = todoService.getOne(new BrTodoQueryDTO().setExtCorpId(extCorpId).setBusinessId(ruleId)
                .setBusinessId1(String.valueOf(jobId)).setType(BrTodo.SOP_TYPE)
                .setOwnerExtId(staffId).setCreateTime(executeAt));

        result.setTodoStatus(brTodo.getStatus());
        result.setTodoDeadlineTime(brTodo.getDeadlineTime());

        return result;
    }

    @Override
    public void createTaskForAddFriend(WxCustomer customer){
        //查询是否存在添加好友触发的sop
        List<BrSop> brSopList = queryList(new BrSopQueryDTO().setExtCorpId(customer.getExtCorpId()).setStatus(BrSop.ENABLE_STATUS).setTerm(SopTermEnum.ADD_FRIEND.getCode()));
        if (ListUtils.isEmpty(brSopList)) {
           return;
        }
        //查询sop下的规则
        List<BrSopRule> ruleList = sopRuleService.list(new LambdaQueryWrapper<BrSopRule>().eq(BrSopRule::getExtCorpId, customer.getExtCorpId())
                .in(BrSopRule::getSopId, brSopList.stream().map(e -> e.getId()).collect(Collectors.toList())));
        for (BrSopRule brSopRule : ruleList) {
            Integer way = brSopRule.getWay();
            Date executeTime = DateUtils.getDate(customer.getCreatedAt(), brSopRule.getStartDay(), brSopRule.getStartTime());
            if (DateUtils.belongCalendarBefore(executeTime)){
                //原有的定时任务id集合
                List<Integer> ruleJobIds = brSopRule.getJobId();
                XxlJobQueryDto dto = new XxlJobQueryDto();
                dto.setIds(ruleJobIds).setTriggerStatus(XxlJobQueryDto.STATUS_ENABLE).setScheduleConf(DateUtils.getCron(executeTime));
                List<XxlJobInfo> list = xxlJobService.getList(dto);
                if (ListUtils.isEmpty(list)){
                    //原本没有定时任务，生成
                    BrSopRuleParamDto paramDto = new BrSopRuleParamDto();
                    BeanUtils.copyProperties(brSopRule,paramDto);
                    BrSop brSop = getById(brSopRule.getSopId());
                    paramDto.setSopName(brSop!=null?brSop.getName():"");
                    Map<String,List<String>> idMap = new HashMap<>();
                    idMap.put(customer.getExtCreatorId(), Arrays.asList(customer.getExtId()));
                    Integer jobId = null;
                    //执行方式为群发，创建群发定时任务
                    if (SopRuleWayEnum.MASS.getCode().equals(way)) {
                        jobId = createXxlJob(idMap,executeTime,paramDto, Constants.WX_MSG_TASK_HANDLER,false,BrSopMsgTaskDto.SOP_TYPE);
                    } else if (SopRuleWayEnum.POST.getCode().equals(way)||SopRuleWayEnum.REMIND.getCode().equals(way)){
                        //执行方式为仅提醒或发朋友圈，创建应用内提醒定时任务
                        jobId = createXxlJob(idMap, executeTime, paramDto, Constants.SEND_MSG_HANDLER,false,BrSopMsgTaskDto.SOP_TYPE);
                    }
                    //启用任务
                    xxlJobService.start(jobId);
                    List<Integer> jobIdList = Optional.ofNullable(brSopRule.getJobId()).orElse(new ArrayList<>());
                    jobIdList.add(jobId);
                    //更新jobId字段
                    sopRuleService.update(new UpdateWrapper<BrSopRule>().lambda()
                            .eq(BrSopRule::getId,brSopRule.getId())
                            .set(BrSopRule::getJobId,jobIdList.toString()));
                }else {
                    //原本有定时任务，更新该定时任务参数
                    XxlJobInfo xxlJobInfo = list.get(0);
                    BrSopMsgTaskDto brSopMsgTaskDto = JSON.parseObject(xxlJobInfo.getExecutorParam(), BrSopMsgTaskDto.class);
                    Map<String, List<String>> idMap = brSopMsgTaskDto.getIdMap();
                    String staffId = customer.getExtCreatorId();
                    if (idMap.containsKey(staffId)){
                        List<String> listByStaff = idMap.get(staffId);
                        listByStaff.add(customer.getExtId());
                        idMap.put(staffId,listByStaff.stream().distinct().collect(Collectors.toList()));
                    }else {
                        idMap.put(customer.getExtCreatorId(), Arrays.asList(customer.getExtId()));
                    }
                    brSopMsgTaskDto.setIdMap(idMap);
                    xxlJobInfo.setExecutorParam(JSON.toJSONString(brSopMsgTaskDto));
                    xxlJobService.update(xxlJobInfo);
                }

            }
        }
    }

    @Override
    public List<BrSopRuleDetailVO> getExecuteDetail(String sopId) {
        List<BrSopRuleDetailVO> result =  new ArrayList<>();
        List<BrSopRule> list = sopRuleService.list(new LambdaQueryWrapper<BrSopRule>()
                .eq(BrSopRule::getSopId, sopId).orderByAsc(BrSopRule::getCreatedAt));

        //查询发送结果
        WxCpExternalContactServiceImpl externalContactService = new WxCpExternalContactServiceImpl(WxCpConfiguration.getCustomerSecretWxCpService());

        for (BrSopRule sopRule : list) {
            List<BrSopDetail> detailList = detailService.list(new LambdaQueryWrapper<BrSopDetail>()
                    .eq(BrSopDetail::getRuleId, sopRule.getId()));

            getSendStatus(sopRule, detailList, externalContactService);

            BrSopRuleDetailVO vo = new BrSopRuleDetailVO();
            BeanUtils.copyProperties(sopRule,vo);
            vo.setRuleName(sopRule.getName());
            List<BrSopRuleExecuteVO> executeVOS = new ArrayList<>();

            //根据执行时间分组
            Map<Date, List<BrSopDetail>> listByTime = detailList.stream().collect(Collectors.groupingBy(BrSopDetail::getExecuteAt));

            listByTime.forEach((executeTime,sopDetailList)->{
                BrSopRuleExecuteVO executeVO = new BrSopRuleExecuteVO();
                executeVO.setExecuteAt(executeTime);

                //根据员工分组
                Map<String, List<BrSopDetail>> listByStaff = sopDetailList.stream().collect(Collectors.groupingBy(BrSopDetail::getExtStaffId));
                Set<String> staffSet = listByStaff.keySet();

                executeVO.setDoneNum(staffSet.size());
                executeVO.setNotDoneNum((int)sopDetailList.stream().filter(e-> !WxMsgSendStatusEnum.STATUS_SEND.getCode().equals(e.getSendStatus())).map(e->e.getExtStaffId()).distinct().count());
                executeVO.setCustomerNum(sopDetailList.size());

                executeVOS.add(executeVO);
            });
            Collections.sort(executeVOS, Comparator.comparing(BrSopRuleExecuteVO::getExecuteAt,Comparator.reverseOrder()));
            vo.setExecuteDetailList(executeVOS);
            result.add(vo);
        }
        return result;
    }

    /**
     * 获取最新群发状态
     */
    private void getSendStatus(BrSopRule sopRule,List<BrSopDetail> detailList, WxCpExternalContactServiceImpl externalContactService){


        if (SopRuleWayEnum.MASS.getCode().equals(sopRule.getWay())){
            //需要查询的detail集合
            List<BrSopDetail> needDetailList = detailList.stream().filter(e -> StringUtils.isNotBlank(e.getExtMsgId())
                    && WxMsgSendStatusEnum.STATUS_NO_SEND.getCode().equals(e.getSendStatus()))
                    .collect(Collectors.toList());
            if (ListUtils.isEmpty(needDetailList)){
                return;
            }

            //更新群发的detail
            Map<String, List<BrSopDetail>> msgMap = needDetailList.stream().collect(Collectors.groupingBy(BrSopDetail::getExtMsgId));

            msgMap.forEach((msgId, details) -> {
                //获取发送结果
                List<WxCpGroupMsgSendResult.ExternalContactGroupMsgSendInfo> sendResult = wxMsgTemplateService.getSendResult(externalContactService, msgId, details.get(0).getExtStaffId(), 1000, null);
                //按客户id分组
                Map<String, WxCpGroupMsgSendResult.ExternalContactGroupMsgSendInfo> sendResultMap = sendResult.stream().collect(Collectors.toMap(WxCpGroupMsgSendResult.ExternalContactGroupMsgSendInfo::getExternalUserId, Function.identity(), (k1, k2) -> k1));
                //更新到我们的数据库
                details.forEach(e -> {
                    WxCpGroupMsgSendResult.ExternalContactGroupMsgSendInfo info = sendResultMap.get(e.getExtCustomerId());
                    if (info != null) {
                        e.setSendStatus(info.getStatus());
                    }
                });
            });

        }
    }

    @Override
    public List<BrSopExecuteDetailVO> getSendDetail(SendDetailQueryDTO dto) {
        BrSopRule sopRule = sopRuleService.getById(dto.getRuleId());
        List<BrSopDetail> detailList = detailService.list(new LambdaQueryWrapper<BrSopDetail>()
                .eq(BrSopDetail::getRuleId, sopRule.getId()).eq(BrSopDetail::getExecuteAt, dto.getExecuteAt()));
        if (ListUtils.isEmpty(detailList)){
            return new ArrayList<>();
        }

        getSendResult(sopRule, detailList);
        List<BrSopExecuteDetailVO> executeDetailList = new ArrayList<>();

        //根据员工分组
        Map<String, List<BrSopDetail>> listByStaff = detailList.stream().collect(Collectors.groupingBy(BrSopDetail::getExtStaffId));
        Set<String> staffSet = listByStaff.keySet();

        for (String staffId : staffSet) {
            BrSopExecuteDetailVO detailVO = new BrSopExecuteDetailVO();
            List<BrSopDetail> details = listByStaff.get(staffId);
            detailVO.setCustomerNum(details.size());
            BrSopDetail detail = details.get(0);
            detailVO.setStaff(staffService.find(detail.getExtCorpId(),staffId));
            detailVO.setSendStatus(detail.getSendStatus());
            detailVO.setSendTime(detail.getSendTime());

            BrTodo brTodo = todoService.getOne(new BrTodoQueryDTO().setExtCorpId(detail.getExtCorpId()).setBusinessId(dto.getRuleId())
                    .setBusinessId1(String.valueOf(detail.getJobId())).setType(BrTodo.SOP_TYPE)
                    .setOwnerExtId(staffId).setCreateTime(DateUtils.dateToStr(dto.getExecuteAt())));

            detailVO.setTodoStatus(brTodo.getStatus());
            detailVO.setTodoDeadlineTime(brTodo.getDeadlineTime());

            executeDetailList.add(detailVO);
        }

        return executeDetailList;
    }

    /**
     * 获取群发详情
     */
    private void getSendResult(BrSopRule sopRule,List<BrSopDetail> detailList){
        //执行方式为群发
        if (SopRuleWayEnum.MASS.getCode().equals(sopRule.getWay())){
            //需要查询的detail集合
            List<BrSopDetail> needDetailList = detailList.stream().filter(e -> StringUtils.isNotBlank(e.getExtMsgId())
                    && WxMsgSendStatusEnum.STATUS_NO_SEND.getCode().equals(e.getSendStatus()))
                    .collect(Collectors.toList());

            if (ListUtils.isEmpty(needDetailList)){
                return;
            }
            //更新群发的detail
            Map<String, List<BrSopDetail>> msgMap = needDetailList.stream().collect(Collectors.groupingBy(BrSopDetail::getExtMsgId));

            //查询发送结果
            WxCpExternalContactServiceImpl externalContactService = new WxCpExternalContactServiceImpl(WxCpConfiguration.getCustomerSecretWxCpService());
            msgMap.forEach((msgId, details) -> {
                //获取发送结果
                List<WxCpGroupMsgSendResult.ExternalContactGroupMsgSendInfo> sendResult = wxMsgTemplateService.getSendResult(externalContactService, msgId, details.get(0).getExtStaffId(), 1000, null);
                //按客户id分组
                Map<String, WxCpGroupMsgSendResult.ExternalContactGroupMsgSendInfo> sendResultMap = sendResult.stream().collect(Collectors.toMap(WxCpGroupMsgSendResult.ExternalContactGroupMsgSendInfo::getExternalUserId, Function.identity(), (k1, k2) -> k1));
                //更新到我们的数据库
                details.forEach(e -> {
                    WxCpGroupMsgSendResult.ExternalContactGroupMsgSendInfo info = sendResultMap.get(e.getExtCustomerId());
                    if (info != null) {
                        e.setSendStatus(info.getStatus());
                        if (info.getSendTime() != null) {
                            e.setSendTime(new Date(info.getSendTime() * 1000));
                        }
                    }
                });
                detailService.updateBatchById(details);
            });

        }
    }

    @Override
    public void updateSendStatus(BrSopUpdateSendStatusDTO dto) {
        detailService.update(new LambdaUpdateWrapper<BrSopDetail>()
                .eq(BrSopDetail::getExtStaffId, dto.getStaffId())
                .eq(BrSopDetail::getRuleId, dto.getRuleId())
                .eq(BrSopDetail::getJobId, dto.getJobId())
                .eq(BrSopDetail::getExecuteAt, dto.getExecuteAt())
                .set(BrSopDetail::getSendStatus, dto.getSendStatus())
                .set(BrSopDetail::getSendTime, new Date())
        );

        todoService.update(new LambdaUpdateWrapper<BrTodo>()
                .eq(BrTodo::getOwnerExtId, dto.getStaffId())
                .eq(BrTodo::getBusinessId, dto.getRuleId())
                .eq(BrTodo::getBusinessId1, dto.getJobId())
                .eq(BrTodo::getCreateTime, dto.getExecuteAt())
                .set(BrTodo::getStatus,BrTodo.DONE_STATUS)
        );

    }

    @Override
    public void remind(BrSopRemindDTO dto) {
        List<BrSopDetail> details = detailService.list(new LambdaQueryWrapper<BrSopDetail>()
                .eq(BrSopDetail::getExtCorpId, dto.getExtCorpId())
                .eq(BrSopDetail::getExtStaffId, dto.getStaffId())
                .eq(BrSopDetail::getRuleId, dto.getRuleId())
                .eq(BrSopDetail::getExecuteAt, dto.getExecuteAt()));

        String REMAIN_TIPS = "【任务提醒】您有任务还未完成！\n" +
                "\n" +
                "任务名称：%s\n" +
                "\n" +
                "创建时间：%s\n" +
                "\n" +
                "可在历史信息中查询详情，记得及时完成哦";

        String content = String.format(REMAIN_TIPS,details.get(0).getSopName(),
                new SimpleDateFormat("YYYY-MM-dd HH:mm").format(dto.getExecuteAt()));
        WxMsgUtils.sendMessage(dto.getExtCorpId(),content, Arrays.asList(dto.getStaffId()));

    }

    @Override
    public Integer countCustomer(BrSopCountCustomerDTO dto) {
        //过滤用户
        WxMsgTemplateCountCustomerDTO wxMsgTemplateCountCustomerDTO = new WxMsgTemplateCountCustomerDTO();
        BeanUtils.copyProperties(dto,wxMsgTemplateCountCustomerDTO);
        wxMsgTemplateCountCustomerDTO.setHasAllCustomer(false);
        wxMsgTemplateCountCustomerDTO.setChooseTagType(WxMsgTemplate.CHOOSE_TYPE_ANY);
        List<String> staffIds = staffService.getStaffIdsByDepts(dto.getExtCorpId(), Optional.ofNullable(dto.getDepartmentIds()).orElse(new ArrayList<>()).
                stream().map(Long::parseLong).collect(Collectors.toList()), dto.getStaffIds());
        if (ListUtils.isEmpty(staffIds)){
            wxMsgTemplateCountCustomerDTO.setHasAllStaff(true);
        }else {
            wxMsgTemplateCountCustomerDTO.setHasAllStaff(false);
            wxMsgTemplateCountCustomerDTO.setStaffIds(staffIds);
        }

        return wxMsgTemplateService.countCustomer(wxMsgTemplateCountCustomerDTO);
    }
}
